﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_GL_QUERY_OBJECT_H
#define _UXS_GL_QUERY_OBJECT_H

#pragma once

#include "GLStateManager.h"

namespace UX_STL
{
namespace GLShell
{

	//
	// Query Object
	//

	class UX_EXPORT GLQuery : public GLBaseObject
	{
	protected:
		GLuint			_uID;
		gl_query::type	_eTarget;

	public:
		GLQuery();
		GLQuery(GLQuery &);
		~GLQuery();

		bool Create();
		void Destroy();

		void Begin(gl_query::type eTarget);
		void End() const;

		void BeginIndexed(gl_query::type eTarget, uint uIndex);
		void EndIndexed(uint uIndex) const;

		void BeginConditionalRender(gl_cond_render::type eMode) const;
		void EngConditionalRender() const;

		void Timestamp();
		void WaitResult() const;

		bool	IsReady()	const;
		uint32	Result()	const;
		uint64	Result64()	const;

		gl_query::type	Target()	const		{ return _eTarget; }
		GLuint			Id()		const		{ return _uID; }

		static GLuint	s_GetActiveQuery(gl_query::type eTarget);
		static int32	s_GetCounterBits(gl_query::type eTarget);
		static uint64	s_GPUTime();
	};
	


	//
	// Sync Object
	//

	class UX_EXPORT GLSyncObject : public GLBaseObject
	{
	protected:
		GLsync	_pSync;

	public:
		GLSyncObject();
		GLSyncObject(GLSyncObject &);
		~GLSyncObject();

		bool Create();
		void Destroy();

		void Wait() const;
		bool ClientWait(uint64 uTime) const;
		bool IsSignaled() const;

		uint64 MaxTimeout() const;

		GLsync	Id()	const	{ return _pSync; }
	};



	//
	// System Sync
	//

	class UX_EXPORT GLSystemSync : public GLBaseObject
	{
	public:
		static void Flush();
		static void Finish();
		static void MemoryBarrier(gl_memory_barrier::type eBarrier);
	};





//----------------------------- GLQuery ---------------------------//
/*
=================================================
	начать замеры
=================================================
*/
	inline void GLQuery::Begin(gl_query::type eTarget)
	{
		/* targets:
			GL_SAMPLES_PASSED		- количество прошедших тест глубины фрагментов
			GL_ANY_SAMPLES_PASSED	- прошел ли хоть один фрагмент тест глубины (GL 3.3)
			GL_TIME_ELAPSED			- замер времени (в наносекундах)
			GL_PRIMITIVES_GENERATED	- количество примитивов дошедших до стадии transform feedback
			GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN - количество записанных примитивов
		*/
		_eTarget = eTarget;
		GL_CALL( glBeginQuery( _eTarget, _uID ) );
	}
	
/*
=================================================
	закончить замеры
=================================================
*/
	inline void GLQuery::End() const
	{
		GL_CALL( glEndQuery( _eTarget ) );
	}
	
/*
=================================================
	начать замеры
	(используется только для transform feedback)
=================================================
*/
	inline void GLQuery::BeginIndexed(gl_query::type eTarget, uint uIndex)
	{
		_eTarget = eTarget;
		GL_CALL( glBeginQueryIndexed( _eTarget, uIndex, _uID ) );
	}
	
/*
=================================================
	закончить замеры
	(используется только для transform feedback)
=================================================
*/
	inline void GLQuery::EndIndexed(uint uIndex) const
	{
		GL_CALL( glEndQueryIndexed( _eTarget, uIndex ) );
	}
	
/*
=================================================
	включить условный рендеринг,
	используется результат запроса
=================================================
*/
	inline void GLQuery::BeginConditionalRender(gl_cond_render::type eMode) const
	{
		/*
		GL_QUERY_WAIT				- ожидать готовности результата
		GL_QUERY_NO_WAIT			- если результат не готов, не осуществлять рендеринг данных
		GL_QUERY_BY_REGION_WAIT		- если результат не готов, то могут использоваться уже готовые результаты
									  или идет ожидание результата
		GL_QUERY_BY_REGION_NO_WAIT	- если результат не готов, то могут использоваться уже готовые результаты,
									  вся остальная геометрия сразу же выводится
		*/
		GL_CALL( glBeginConditionalRender( _uID, eMode ) );
	}
	
/*
=================================================
	отключить условный рендеринг
=================================================
*/
	inline void GLQuery::EngConditionalRender() const
	{
		GL_CALL( glEndConditionalRender() );
	}

/*
=================================================
	асинхронно получить текущее время GPU в наносекундах
=================================================
*/
	inline void GLQuery::Timestamp()
	{
		_eTarget = gl_query::TIMESTAMP;
		GL_CALL( glQueryCounter( _eTarget, _uID ) );
	}
	
/*
=================================================
	получить текущее время GPU в наносекундах
=================================================
*/
	inline uint64 GLQuery::s_GPUTime()
	{
		GLint64	i_ret = 0;
		GL_CALL( glGetInteger64v( gl_query::TIMESTAMP, &i_ret ) );
		return i_ret;
	}

/*
=================================================
	ждать пока не будет готов результат
=================================================
*/
	inline void GLQuery::WaitResult() const
	{
		GLint	i_ready = GL_FALSE;

		while ( i_ready != GL_TRUE )
			GL_CALL( glGetQueryObjectiv( _uID, GL_QUERY_RESULT_AVAILABLE, &i_ready ) );
	}

/*
=================================================
	проверка готов ли результат
=================================================
*/
	inline bool GLQuery::IsReady() const
	{
		GLint	i_ready = 0;
		GL_CALL( glGetQueryObjectiv( _uID, GL_QUERY_RESULT_AVAILABLE, &i_ready ) );
		return i_ready == GL_TRUE;
	}
	
/*
=================================================
	возвращает результат замера
=================================================
*/
	inline uint32 GLQuery::Result() const
	{
		GLuint	u_result = 0;
		GL_CALL( glGetQueryObjectuiv( _uID, GL_QUERY_RESULT, &u_result ) );
		return u_result;
	}
	
/*
=================================================
	возвращает результат замера
=================================================
*/
	inline uint64 GLQuery::Result64() const
	{
		GLuint64	u_result = 0;
		GL_CALL( glGetQueryObjectui64v( _uID, GL_QUERY_RESULT, &u_result ) );
		return u_result;
	}
	
/*
=================================================
	возвращает идентификатор активного объекта
=================================================
*/
	inline GLuint GLQuery::s_GetActiveQuery(gl_query::type eTarget)
	{
		GLint	i_active = 0;
		GL_CALL( glGetQueryiv( eTarget, GL_CURRENT_QUERY, &i_active ) );
		return i_active;
	}
	
/*
=================================================
	возвращает разрядность счетчика
=================================================
*/
	inline int32 GLQuery::s_GetCounterBits(gl_query::type eTarget)
	{
		GLint	i_bits = 0;
		GL_CALL( glGetQueryiv( eTarget, GL_QUERY_COUNTER_BITS, &i_bits ) );
		return i_bits;
	}


	
//-------------------------- GLSyncObject -------------------------//
/*
=================================================
	
=================================================
*/
	inline void GLSyncObject::Wait() const
	{
		ASSERT( _pSync != null );
		GL_CALL( glWaitSync( _pSync, 0, GL_TIMEOUT_IGNORED ) );
	}
	
/*
=================================================
	
=================================================
*/
	inline bool GLSyncObject::ClientWait(uint64 uTime) const
	{
		ASSERT( _pSync != null );

		GLenum	res = 0;
		GL_CALL( res = glClientWaitSync( _pSync, GL_SYNC_FLUSH_COMMANDS_BIT, uTime ) );
		/*
		GL_ALREADY_SIGNALED		indicates that sync was signaled at the time that glClientWaitSync was called. 
		GL_TIMEOUT_EXPIRED		indicates that at least timeout nanoseconds passed and sync did not become signaled. 
		GL_CONDITION_SATISFIED	indicates that sync was signaled before the timeout expired. 
		GL_WAIT_FAILED			indicates that an error occurred. Additionally, an OpenGL error will be generated.
		*/
		return ( res == GL_ALREADY_SIGNALED or res == GL_CONDITION_SATISFIED );
	}
	
/*
=================================================
	
=================================================
*/
	inline uint64 GLSyncObject::MaxTimeout() const
	{
		GLint64	i_time = 0;
		GL_CALL( glGetInteger64v( GL_MAX_SERVER_WAIT_TIMEOUT, &i_time ) );
		return i_time;
	}
	
/*
=================================================
	
=================================================
*/
	inline bool GLSyncObject::IsSignaled() const
	{
		GLint	i_val = 0;
		GL_CALL( glGetSynciv( _pSync, GL_SYNC_STATUS, 1, null, &i_val ) );
		return i_val == GL_SIGNALED;
	}
	


//-------------------------- GLSystemSync -------------------------//
/*
=================================================
	
=================================================
*/
	inline void GLSystemSync::Flush()
	{
		GL_CALL( glFlush() );
	}

/*
=================================================
	
=================================================
*/
	inline void GLSystemSync::Finish()
	{
		GL_CALL( glFinish() );
	}
	
/*
=================================================
	
=================================================
*/
	inline void GLSystemSync::MemoryBarrier(gl_memory_barrier::type eBarriers)
	{
		GL_CALL( glMemoryBarrier( eBarriers ) );
	}

//-------------------------------------------------------------------

}	// GLShell
}	// UX_STL

#endif	// _UXS_GL_QUERY_OBJECT_H